home *** CD-ROM | disk | FTP | other *** search
/ SGI Hot Mix 17 / Hot Mix 17.iso / HM17_SGI / research / examples / object / surf_track.pro < prev    next >
Text File  |  1997-07-08  |  19KB  |  473 lines

  1. ;
  2. ; $Id: surf_track.pro,v 1.2 1997/03/26 20:40:21 griz Exp $
  3. ;
  4. ; Copyright (c) 1997, Research Systems, Inc.  All rights reserved.
  5. ;    Unauthorized reproduction prohibited.
  6. ;+
  7. ; NAME:
  8. ;    SURF_TRACK
  9. ;
  10. ; PURPOSE:
  11. ;    This procedure serves as an example of using the trackball
  12. ;    object to manipulate a surface object.
  13. ;
  14. ; CATEGORY:
  15. ;    Object graphics.
  16. ;
  17. ; CALLING SEQUENCE:
  18. ;    SURF_TRACK, [zData]
  19. ;
  20. ; OPTIONAL INPUTS:
  21. ;     zData: A two-dimensional floating point array representing 
  22. ;              the data to be displayed as a surface.  By default,
  23. ;              a BESEL function is displayed.
  24. ;
  25. ; MODIFICATION HISTORY:
  26. ;     Written by:    DD, June 1996
  27. ;-
  28.  
  29. ;----------------------------------------------------------------------------
  30. FUNCTION Toggle_State, wid
  31.  
  32.     WIDGET_CONTROL, wid, GET_VALUE=name
  33.  
  34.     s = STRPOS(name, '(off)')
  35.     IF (s NE -1) THEN BEGIN
  36.         STRPUT, name, '(on) ', s
  37.         ret = 1
  38.     ENDIF ELSE BEGIN
  39.         s = STRPOS(name, '(on) ')
  40.         STRPUT, name, '(off)',s
  41.         ret = 0
  42.     ENDELSE
  43.  
  44.     WIDGET_CONTROL, wid, SET_VALUE=name
  45.     RETURN, ret
  46. END
  47.  
  48. ;----------------------------------------------------------------------------
  49. PRO SURF_TRACK_EVENT, sEvent
  50.  
  51.     WIDGET_CONTROL, sEvent.id, GET_UVALUE=uval
  52.  
  53.     ; Handle KILL requests.
  54.     IF TAG_NAMES(sEvent, /STRUCTURE_NAME) EQ 'WIDGET_KILL_REQUEST' THEN BEGIN
  55.         WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState
  56.  
  57.        ; Destroy the objects.
  58.        OBJ_DESTROY, sState.oHolder
  59.        WIDGET_CONTROL, sEvent.top, /DESTROY
  60.        RETURN
  61.     ENDIF
  62.  
  63.     ; Handle other events.
  64.     CASE uval OF
  65.         'STYLE': BEGIN
  66.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  67.             sState.oSurface->SetProperty, STYLE=sEvent.index
  68.             CASE sEvent.index OF
  69.                 0: BEGIN
  70.                        WIDGET_CONTROL, sState.wHide, SENSITIVE=1
  71.                        WIDGET_CONTROL, sState.wShading, SENSITIVE=0
  72.                    END 
  73.                 1: BEGIN
  74.                        WIDGET_CONTROL, sState.wHide, SENSITIVE=1
  75.                        WIDGET_CONTROL, sState.wShading, SENSITIVE=0
  76.                    END 
  77.                 2: BEGIN
  78.                        WIDGET_CONTROL, sState.wHide, SENSITIVE=0
  79.                        WIDGET_CONTROL, sState.wShading, SENSITIVE=1
  80.                    END 
  81.                 3: BEGIN
  82.                        WIDGET_CONTROL, sState.wHide, SENSITIVE=1
  83.                        WIDGET_CONTROL, sState.wShading, SENSITIVE=0
  84.                    END 
  85.                 4: BEGIN
  86.                        WIDGET_CONTROL, sState.wHide, SENSITIVE=1
  87.                        WIDGET_CONTROL, sState.wShading, SENSITIVE=0
  88.                    END 
  89.                 5: BEGIN
  90.                        WIDGET_CONTROL, sState.wHide, SENSITIVE=1
  91.                        WIDGET_CONTROL, sState.wShading, SENSITIVE=0
  92.                    END 
  93.                 6: BEGIN
  94.                        WIDGET_CONTROL, sState.wHide, SENSITIVE=0
  95.                        WIDGET_CONTROL, sState.wShading, SENSITIVE=1
  96.                    END 
  97.             ENDCASE 
  98.             sState.oWindow->Draw, sState.oView
  99.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  100.           END
  101.         'MM_MIN0': BEGIN
  102.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  103.             sState.oSurface->SetProperty, MIN_VALUE=sState.zMinVals[0]
  104.             sState.oWindow->Draw, sState.oView
  105.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  106.           END
  107.         'MM_MIN1': BEGIN
  108.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  109.             sState.oSurface->SetProperty, MIN_VALUE=sState.zMinVals[1]
  110.             sState.oWindow->Draw, sState.oView
  111.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  112.           END
  113.         'MM_MIN2': BEGIN
  114.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  115.             sState.oSurface->SetProperty, MIN_VALUE=sState.zMinVals[2]
  116.             sState.oWindow->Draw, sState.oView
  117.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  118.           END
  119.         'MM_MAX0': BEGIN
  120.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  121.             sState.oSurface->SetProperty, MAX_VALUE=sState.zMaxVals[0]
  122.             sState.oWindow->Draw, sState.oView
  123.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  124.           END
  125.         'MM_MAX1': BEGIN
  126.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  127.             sState.oSurface->SetProperty, MAX_VALUE=sState.zMaxVals[1]
  128.             sState.oWindow->Draw, sState.oView
  129.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  130.           END
  131.         'MM_MAX2': BEGIN
  132.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  133.             sState.oSurface->SetProperty, MAX_VALUE=sState.zMaxVals[2]
  134.             sState.oWindow->Draw, sState.oView
  135.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  136.           END
  137.         'SHADE_FLAT': BEGIN
  138.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  139.             sState.oSurface->SetProperty, SHADING=0
  140.             sState.oWindow->Draw, sState.oView    
  141.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  142.           END
  143.         'SHADE_GOURAUD': BEGIN
  144.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  145.             sState.oSurface->SetProperty, SHADING=1
  146.             sState.oWindow->Draw, sState.oView    
  147.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  148.           END
  149.         'VC_OFF': BEGIN
  150.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  151.             wParent = WIDGET_INFO(sEvent.id, /PARENT)
  152.             j = Toggle_State(wParent)
  153.             sState.oSurface->SetProperty, VERT_COLORS=0
  154.             sState.oWindow->Draw, sState.oView    
  155.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  156.           END
  157.         'VC_ON': BEGIN
  158.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  159.             wParent = WIDGET_INFO(sEvent.id, /PARENT)
  160.             j = Toggle_State(wParent)
  161.         sState.oSurface->SetProperty, VERT_COLORS=sState.vc
  162.             sState.oWindow->Draw, sState.oView    
  163.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  164.           END
  165.         'HIDE_OFF': BEGIN
  166.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY            
  167.             wParent = WIDGET_INFO(sEvent.id, /PARENT)
  168.             j = Toggle_State(wParent)
  169.             sState.oSurface->SetProperty, HIDDEN_LINES=0
  170.             sState.oWindow->Draw, sState.oView    
  171.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  172.           END
  173.         'HIDE_ON': BEGIN
  174.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY            
  175.             wParent = WIDGET_INFO(sEvent.id, /PARENT)
  176.             j = Toggle_State(wParent)
  177.             sState.oSurface->SetProperty, HIDDEN_LINES=1
  178.             sState.oWindow->Draw, sState.oView    
  179.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  180.           END
  181.         'SKIRT0': BEGIN
  182.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  183.             sState.oSurface->SetProperty, SHOW_SKIRT=0
  184.             sState.oWindow->Draw, sState.oView    
  185.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  186.           END
  187.         'SKIRT1': BEGIN
  188.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  189.             sState.oSurface->SetProperty, SKIRT=sState.zSkirts[0], $
  190.                                           /SHOW_SKIRT
  191.             sState.oWindow->Draw, sState.oView    
  192.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  193.           END
  194.         'SKIRT2': BEGIN
  195.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  196.             sState.oSurface->SetProperty, SKIRT=sState.zSkirts[1], $
  197.                                           /SHOW_SKIRT
  198.             sState.oWindow->Draw, sState.oView    
  199.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  200.           END
  201.         'SKIRT3': BEGIN
  202.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  203.             sState.oSurface->SetProperty, SKIRT=sState.zSkirts[2], $
  204.                                           /SHOW_SKIRT
  205.             sState.oWindow->Draw, sState.oView    
  206.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  207.           END
  208.         'DRAGQ0' : BEGIN
  209.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  210.             sState.dragq = 0
  211.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  212.           END
  213.         'DRAGQ1' : BEGIN
  214.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  215.             sState.dragq = 1
  216.             WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  217.           END
  218.         'DRAW': BEGIN
  219.             WIDGET_CONTROL, sEvent.top, GET_UVALUE=sState, /NO_COPY
  220.  
  221.             ; Expose.
  222.             IF (sEvent.type EQ 4) THEN BEGIN
  223.                 sState.oWindow->Draw, sState.oView
  224.                 WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  225.                 RETURN
  226.             ENDIF
  227.  
  228.            ; Handle trackball updates.
  229.            bHaveTransform = sState.oTrack->Update( sEvent, TRANSFORM=qmat )
  230.            IF (bHaveTransform NE 0) THEN BEGIN
  231.                sState.oGroup->GetProperty, TRANSFORM=t
  232.                sState.oGroup->SetProperty, TRANSFORM=t#qmat
  233.                sState.oWindow->Draw, sState.oView
  234.            ENDIF
  235.  
  236.            ; Handle other events: PICKING, quality changes, etc.
  237.            ;  Button press.
  238.            IF (sEvent.type EQ 0) THEN BEGIN
  239.                IF (sEvent.press EQ 4) THEN BEGIN ; Right mouse.
  240.                pick = sState.oWindow->PickData(sState.oView,$
  241.                                                    sState.oSurface, $
  242.                                                    [sEvent.x,sEvent.y],dataxyz)
  243.                IF (pick EQ 1) THEN BEGIN
  244.                str = STRING(dataxyz[0],dataxyz[1],dataxyz[2], $
  245.                 FORMAT='("Data point: X=",F7.3,",Y=",F7.3,",Z=",F7.3)')
  246.                WIDGET_CONTROL, sState.wLabel, SET_VALUE=str
  247.                ENDIF ELSE BEGIN
  248.                WIDGET_CONTROL, sState.wLabel, $
  249.                             SET_VALUE="Data point: In background."
  250.                    ENDELSE
  251.  
  252.                    sState.btndown = 4b
  253.                WIDGET_CONTROL, sState.wDraw, /DRAW_MOTION
  254.                ENDIF ELSE BEGIN ; other mouse button.
  255.                    sState.btndown = 1b
  256.                    sState.oWindow->SetProperty, QUALITY=sState.dragq
  257.                    WIDGET_CONTROL, sState.wDraw, /DRAW_MOTION
  258.                    sState.oWindow->Draw, sState.oView
  259.                ENDELSE  
  260.           ENDIF
  261.  
  262.          ; Button motion.
  263.          IF (sEvent.type EQ 2) THEN BEGIN
  264.              IF (sState.btndown EQ 4b) THEN BEGIN ; Right mouse button.
  265.              pick = sState.oWindow->PickData(sState.oView, $
  266.                                                  sState.oSurface, $
  267.                                                  [sEvent.x,sEvent.y], dataxyz)
  268.              IF (pick EQ 1) THEN BEGIN
  269.                      str = STRING(dataxyz[0],dataxyz[1],dataxyz[2], $
  270.                 FORMAT='("Data point: X=",F7.3,",Y=",F7.3,",Z=",F7.3)')
  271.              WIDGET_CONTROL, sState.wLabel, SET_VALUE=str
  272.                  ENDIF ELSE BEGIN
  273.                      WIDGET_CONTROL, sState.wLabel, $
  274.                          SET_VALUE="Data point: In background."
  275.                  ENDELSE
  276.  
  277.              ENDIF
  278.         ENDIF
  279.  
  280.         ; Button release.
  281.         IF (sEvent.type EQ 1) THEN BEGIN
  282.             IF (sState.btndown EQ 1b) THEN BEGIN
  283.                   sState.oWindow->SetProperty, QUALITY=2
  284.                   sState.oWindow->Draw, sState.oView
  285.             ENDIF
  286.             sState.btndown = 0b
  287.             WIDGET_CONTROL, sState.wDraw, DRAW_MOTION=0
  288.         ENDIF
  289.         WIDGET_CONTROL, sEvent.top, SET_UVALUE=sState, /NO_COPY
  290.       END
  291.     ENDCASE
  292. END
  293.  
  294. ;----------------------------------------------------------------------------
  295. PRO SURF_TRACK, zData
  296.  
  297.     xdim = 480
  298.     ydim = 360
  299.  
  300.     ; Default surface data is a Besel function.
  301.     IF (N_ELEMENTS(zData) EQ 0) THEN $
  302.      zData = BESELJ(SHIFT(DIST(40),20,20)/2,0)
  303.  
  304.     ; Compute potential skirt values.
  305.     zMax = MAX(zData, MIN=zMin)
  306.     zQuart = (zMax - zMin) * 0.25
  307.     zSkirts = [zMin-zQuart, zMin, zMin+zQuart]
  308.  
  309.     ; Create the widgets.
  310.     wBase = WIDGET_BASE(/COLUMN, XPAD=0, YPAD=0, $
  311.                         TITLE="Surface Trackball Example", $
  312.                         /TLB_KILL_REQUEST_EVENTS)
  313.  
  314.     wDraw = WIDGET_DRAW(wBase, XSIZE=xdim, YSIZE=ydim, UVALUE='DRAW', $
  315.                         RETAIN=0, /EXPOSE_EVENTS, /BUTTON_EVENTS, $
  316.                         GRAPHICS_LEVEL=2)
  317.     wGuiBase = WIDGET_BASE(wBase, /ROW)
  318.     wStyleDrop = WIDGET_DROPLIST(wGuiBase, VALUE=['Point','Wire','Solid',$
  319.                                  'Ruled XZ','Ruled YZ','Lego Wire', $
  320.                                  'Lego Solid'], /FRAME, $
  321.                                  TITLE='Style', UVALUE='STYLE')
  322.  
  323.     wOptions = WIDGET_BUTTON(wGuiBase, MENU=2, VALUE="Additional Options...")
  324.  
  325.     wDrag = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Drag Quality")
  326.     wButton = WIDGET_BUTTON(wDrag, VALUE='Low', UVALUE='DRAGQ0') 
  327.     wButton = WIDGET_BUTTON(wDrag, VALUE='Medium', UVALUE='DRAGQ1') 
  328.  
  329.     wHide = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Hidden Lines (off)")
  330.     wButton = WIDGET_BUTTON(wHide, VALUE='Off', UVALUE='HIDE_OFF') 
  331.     wButton = WIDGET_BUTTON(wHide, VALUE='On', UVALUE='HIDE_ON') 
  332.  
  333.     wMinMax = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Minimum")
  334.     zMinVals = [zMin, zMin+zQuart, zMin+2*zQuart]
  335.     zLabels = ['Reset', STRCOMPRESS(STRING(zMinVals[1:2]), /REMOVE_ALL)]
  336.     wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[0], UVALUE='MM_MIN0')
  337.     wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[1], UVALUE='MM_MIN1')
  338.     wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[2], UVALUE='MM_MIN2')
  339.     wMinMax = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Maximum")
  340.     zMaxVals = [zMax, zMax-zQuart, zMax-2*zQuart]
  341.     zLabels = ['Reset', STRCOMPRESS(STRING(zMaxVals[1:2]), /REMOVE_ALL)]
  342.     wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[0], UVALUE='MM_MAX0')
  343.     wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[1], UVALUE='MM_MAX1')
  344.     wButton = WIDGET_BUTTON(wMinMax, VALUE=zLabels[2], UVALUE='MM_MAX2')
  345.  
  346.     wShading = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Shading")
  347.     wButton = WIDGET_BUTTON(wShading, VALUE='Flat', UVALUE='SHADE_FLAT') 
  348.     wButton = WIDGET_BUTTON(wShading, VALUE='Gouraud', UVALUE='SHADE_GOURAUD') 
  349.  
  350.     wVC = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Vertex Colors (off)")
  351.     wButton = WIDGET_BUTTON(wVC, VALUE='Off', UVALUE='VC_OFF') 
  352.     wButton = WIDGET_BUTTON(wVC, VALUE='On', UVALUE='VC_ON') 
  353.  
  354.     zLabels = ['None', STRCOMPRESS(STRING(zSkirts[*]), /REMOVE_ALL)]
  355.     wSkirt = WIDGET_BUTTON(wOptions, MENU=2, VALUE="Skirt")
  356.     wButton = WIDGET_BUTTON(wSkirt, VALUE=zLabels[0], UVALUE='SKIRT0')
  357.     wButton = WIDGET_BUTTON(wSkirt, VALUE=zLabels[1], UVALUE='SKIRT1')
  358.     wButton = WIDGET_BUTTON(wSkirt, VALUE=zLabels[2], UVALUE='SKIRT2')
  359.     wButton = WIDGET_BUTTON(wSkirt, VALUE=zLabels[3], UVALUE='SKIRT3')
  360.  
  361.     ; Status line.
  362.     wGuiBase2 = WIDGET_BASE(wBase, /COLUMN)
  363.     wLabel = WIDGET_LABEL(wGuiBase2, /FRAME, $
  364.                   VALUE="Left Mouse: Trackball    Right Mouse: Data Picking" )
  365.     wLabel = WIDGET_LABEL(wGuiBase2, VALUE=" ", /DYNAMIC_RESIZE)
  366.  
  367.     WIDGET_CONTROL, wBase, /REALIZE
  368.  
  369.     ; Get the window id of the drawable.
  370.     WIDGET_CONTROL, wDraw, GET_VALUE=oWindow
  371.  
  372.     ; Set default droplist items.
  373.     WIDGET_CONTROL, wStyleDrop, SET_DROPLIST_SELECT=2
  374.     WIDGET_CONTROL, wHide, SENSITIVE=0
  375.     
  376.     ; Compute viewplane rect based on aspect ratio.
  377.     aspect = FLOAT(xdim) / FLOAT(ydim)
  378.     sqrt2 = SQRT(2.0)
  379.     myview = [ -sqrt2*0.5, -sqrt2*0.5, sqrt2, sqrt2 ]
  380.     IF (aspect > 1) THEN BEGIN
  381.         myview[0] = myview[0] - ((aspect-1.0)*myview[2])/2.0
  382.         myview[2] = myview[2] * aspect
  383.     ENDIF ELSE BEGIN
  384.         myview[1] = myview[1] - (((1.0/aspect)-1.0)*myview[3])/2.0
  385.         myview[3] = myview[3] * aspect
  386.     ENDELSE
  387.  
  388.     ; Create view.  
  389.     oView = OBJ_NEW('IDLgrView', PROJECTION=2, EYE=3, ZCLIP=[1.4,-1.4],$
  390.                     VIEWPLANE_RECT=myview, COLOR=[40,40,40])
  391.  
  392.     ; Create model.
  393.     oTop = OBJ_NEW('IDLgrModel')
  394.     oGroup = OBJ_NEW('IDLgrModel')
  395.     oTop->Add, oGroup
  396.  
  397.     ; Compute data bounds. 
  398.     sz = SIZE(zData)
  399.     xMax = sz[1] - 1
  400.     yMax = sz[2] - 1
  401.     zMin2 = zMin - 1
  402.     zMax2 = zMax + 1 
  403.  
  404.     ; Compute coordinate conversion to normalize.
  405.     xs = [-0.5,1.0/xMax]
  406.     ys = [-0.5,1.0/yMax]
  407.     zs = [(-zMin2/(zMax2-zMin2))-0.5, 1.0/(zMax2-zMin2)]
  408.  
  409.     ; Generate vertex colors to emulate height fields.
  410.     vc = BYTARR(3,sz[1]*sz[2], /NOZERO)
  411.     cbins=[[255,0,0],$
  412.            [255,85,0],$
  413.            [255,170,0],$
  414.            [255,255,0],$
  415.            [170,255,0],$
  416.            [85,255,0],$
  417.            [0,255,0]]
  418.     zi = ROUND((zData - zMin)/(zMax-zMin) * 6.0)  
  419.     vc[*,*] = cbins[*,zi]
  420.  
  421.     ; Create the surface.
  422.     oSurface = OBJ_NEW('IDLgrSurface', zData, STYLE=2, SHADING=0, $
  423.                        COLOR=[60,60,255], BOTTOM=[64,192,128], $
  424.                        XCOORD_CONV=xs, YCOORD_CONV=ys, ZCOORD_CONV=zs)
  425.     oGroup->Add, oSurface
  426.  
  427.     ; Create some lights.
  428.     oLight = OBJ_NEW('IDLgrLight', LOCATION=[2,2,2], TYPE=1)
  429.     oTop->Add, oLight
  430.     oLight = OBJ_NEW('IDLgrLight', TYPE=0, INTENSITY=0.5)
  431.     oTop->Add, oLight
  432.  
  433.     ; Place the model in the view.
  434.     oView->Add, oTop
  435.  
  436.     ; Rotate to standard view for first draw.
  437.     oGroup->Rotate, [1,0,0], -90
  438.     oGroup->Rotate, [0,1,0], 30
  439.     oGroup->Rotate, [1,0,0], 30
  440.  
  441.     ; Create a trackball.
  442.     oTrack = OBJ_NEW('Trackball', [xdim/2, ydim/2.], xdim/2.)
  443.  
  444.     ; Create a holder object for easy destruction.
  445.     oHolder = OBJ_NEW('IDL_Container')
  446.     oHolder->Add, oView
  447.     oHolder->Add, oTrack
  448.  
  449.     ; Save state.
  450.     sState = {btndown: 0b,           $ 
  451.           dragq: 0,             $
  452.               oHolder: oHolder,         $
  453.               oTrack:oTrack,         $ 
  454.               wDraw: wDraw,          $
  455.               wLabel: wLabel,        $
  456.               wHide: wHide,          $
  457.               wShading: wShading,    $  
  458.               oWindow: oWindow,      $
  459.               oView: oView,          $
  460.               oGroup: oGroup,        $
  461.               oSurface: oSurface,    $
  462.               zSkirts: zSkirts,      $
  463.               zMinVals: zMinVals,    $
  464.               zMaxVals: zMaxVals,    $
  465.               vc: vc                 $
  466.              }  
  467.  
  468.     WIDGET_CONTROL, wBase, SET_UVALUE=sState, /NO_COPY
  469.  
  470.     XMANAGER, 'SURF_TRACK', wBase, /NO_BLOCK
  471.  
  472. END
  473.